package com.bosssoft.platform.fasttcc.impl;

import com.bosssoft.platform.fasttcc.Tcc;
import com.bosssoft.platform.fasttcc.TccSimple;
import com.bosssoft.platform.fasttcc.TccOperation;
import com.bosssoft.platform.fasttcc.TccOperationRegistry;
import com.bosssoft.platform.fasttcc.support.BeanClassFinder;
import com.jfireframework.baseutil.StringUtil;
import com.jfireframework.baseutil.reflect.ReflectUtil;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TccOperationRegistryImpl implements TccOperationRegistry
{
    private       Map<Method, TccOperation> store = new ConcurrentHashMap<>();
    private final BeanClassFinder           beanClassFinder;

    public TccOperationRegistryImpl(BeanClassFinder beanClassFinder)
    {
        this.beanClassFinder = beanClassFinder;
    }

    @Override
    public TccOperation get(Method method)
    {
        TccOperation tccOperation = store.get(method);
        if (tccOperation != null)
        {
            return tccOperation;
        }
        if (method.isAnnotationPresent(Tcc.class))
        {
            return findFromTcc(method);
        }
        else if (method.isAnnotationPresent(TccSimple.class))
        {
            return findFromTccSimple(method);
        }
        else
        {
            throw new IllegalArgumentException("方法:" + method.getDeclaringClass().getName() + "." + method.getName() + "上没有TCC注解");
        }
    }

    private TccOperation findFromTccSimple(Method method)
    {
        TccSimple tccSimple     = method.getAnnotation(TccSimple.class);
        Class<?>  confirmClass  = null;
        Class<?>  cancelClass   = null;
        Method    confirmMethod = null;
        Method    cancelMethod  = null;
        if (StringUtil.isNotBlank(tccSimple.confirmMethod()))
        {
            confirmClass = method.getDeclaringClass();
            try
            {
                confirmMethod = method.getDeclaringClass().getDeclaredMethod(tccSimple.confirmMethod(), method.getParameterTypes());
            }
            catch (NoSuchMethodException e)
            {
                ReflectUtil.throwException(e);
            }
        }
        if (StringUtil.isNotBlank(tccSimple.cancelMethod()))
        {
            cancelClass = method.getDeclaringClass();
            try
            {
                cancelMethod = method.getDeclaringClass().getDeclaredMethod(tccSimple.cancelMethod(), method.getParameterTypes());
            }
            catch (NoSuchMethodException e)
            {
                ReflectUtil.throwException(e);
            }
        }
        TccOperationImpl tmp = new TccOperationImpl();
        tmp.setTryMethod(method);
        tmp.setTryClass(method.getDeclaringClass());
        tmp.setCancelMethod(cancelMethod);
        tmp.setCancelClass(cancelClass);
        tmp.setConfirmClass(confirmClass);
        tmp.setConfirmMethod(confirmMethod);
        store.put(method, tmp);
        return tmp;
    }

    private TccOperation findFromTcc(Method method)
    {
        Method   tryMethod     = method;
        Method   confirmMethod = null;
        Method   cancelMethod  = null;
        Class<?> tryClass      = method.getDeclaringClass();
        Class<?> confirmClass  = null;
        Class<?> cancelClass   = null;
        Tcc      annotation    = method.getAnnotation(Tcc.class);
        if (annotation.confirmClass() != Object.class)
        {
            confirmClass = annotation.confirmClass();
        }
        else if (annotation.confirmBeanName().equals("") == false)
        {
            confirmClass = beanClassFinder.find(annotation.confirmBeanName());
        }
        if (confirmClass != null)
        {
            try
            {
                confirmMethod = confirmClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
            }
            catch (NoSuchMethodException e)
            {
                ReflectUtil.throwException(e);
            }
        }
        if (annotation.cancelClass() != Object.class)
        {
            cancelClass = annotation.cancelClass();
        }
        else if (annotation.cancelBeanName().equals("") == false)
        {
            cancelClass = beanClassFinder.find(annotation.cancelBeanName());
        }
        if (cancelClass != null)
        {
            try
            {
                cancelMethod = cancelClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
            }
            catch (NoSuchMethodException e)
            {
                ReflectUtil.throwException(e);
            }
        }
        TccOperationImpl tccOperation = new TccOperationImpl();
        tccOperation.setCancelClass(cancelClass);
        tccOperation.setConfirmClass(confirmClass);
        tccOperation.setTryClass(tryClass);
        tccOperation.setCancelMethod(cancelMethod);
        tccOperation.setConfirmMethod(confirmMethod);
        tccOperation.setTryMethod(tryMethod);
        store.put(method, tccOperation);
        return tccOperation;
    }
}
